﻿using Microsoft.AspNet.Identity;
using mvcForum.Core.Interfaces.Services;
using MVCForum.Web.Identity.EF.Entities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MVCForum.Web.Identity.EF {

	public class MembershipService : IMembershipService {
		private readonly IRoleStore<Role> roleStore;
		private readonly IUserStore<User> userStore;
		private readonly IUserRoleStore<User> userRoleStore;
		private readonly UserManager<User> userManager;

		public MembershipService(IRoleStore<Role> roleStore,
								IUserStore<User> userStore,
								IUserRoleStore<User> userRoleStore,
								UserManager<User> userManager) {

			this.roleStore = roleStore;
			this.userStore = userStore;
			this.userRoleStore = userRoleStore;
			this.userManager = userManager;
		}

		public void AddAccountToRoles(String accountName, String[] roles) {
			IUser user = this.userManager.FindByName(accountName);
			foreach (String roleName in roles) {
				this.userManager.AddToRole(user.Id, roleName);
			}
		}

		public Boolean CreateAccount(String accountName, String password, String emailAddress, out String errorMessage) {
			errorMessage = String.Empty;

			IdentityResult result = this.userManager.Create(new User {
				Approved = false,
				Created = DateTime.UtcNow,
				EmailAddress = emailAddress,
				LastLockout = DateTime.UtcNow,
				LastPasswordFailure = DateTime.UtcNow,
				LastVisit = DateTime.UtcNow,
				Locked = false,
				UserName = accountName
			}, password);

			if (!result.Succeeded) {
				errorMessage = String.Join(", ", result.Errors);
			}
			return result.Succeeded;
		}

		public void CreateRole(String roleName) {
			this.roleStore.CreateAsync(new Role {
				Name = roleName
			});
		}

		public void DeleteAccount(String accountName, Boolean deleteAllRelatedData) {
			User user = this.userManager.FindByName(accountName);
			this.userManager.Delete(user);
		}

		public void DeleteAccount(String accountName) {
			this.DeleteAccount(accountName, true);
		}

		public IAccount GetAccount(Boolean online) {
			throw new NotImplementedException();
		}

		private IAccount GetAccount(User user) {
			return new Account {
				 AccountName = user.UserName,
				 CreationDate = user.Created,
				 EmailAddress = user.EmailAddress,
				 IsApproved = user.Approved,
				 IsLockedOut = user.Locked,
				 LastActivityDate = user.LastVisit,
				 LastLockoutDate = user.LastLockout,
				 LastLoginDate = user.LastVisit,
				 ProviderUserKey = user.Id
			};
		}

		public IAccount GetAccount(Object id) {
			User user = this.userManager.FindById(id.ToString());
			return this.GetAccount(user);
		}

		public IAccount GetAccountByEmailAddress(String emailAddress) {
			User user = this.userManager.FindByEmail(emailAddress);
			return this.GetAccount(user);
		}

		public IAccount GetAccountByName(String accountName, Boolean online) {
			User user = this.userManager.FindByName(accountName);
			// TODO: Update last activity
			return this.GetAccount(user);
		}

		public IAccount GetAccountByName(String accountName) {
			return this.GetAccountByName(accountName, false);
		}

		public String GetAccountNameByEmailAddress(String emailAddress) {
			return this.userManager.FindByEmail(emailAddress).UserName;
		}

		public IEnumerable<IAccount> GetAllAccounts(Int32 page, Int32 pageSize, out Int32 total) {
			total = this.userManager.Users.Count();
			return this.userManager.Users.Skip(page * pageSize).Take(pageSize).Select(u => this.GetAccount(u));
		}

		public String[] GetAllRoles() {
			throw new NotImplementedException();
		}

		public String[] GetRolesForAccount(String accountName) {
			return this.userRoleStore.GetRolesAsync(this.userStore.FindByNameAsync(accountName).Result).Result.ToArray();
		}

		public String[] GetRolesForAccount() {
			throw new NotImplementedException();
		}

		public Boolean IsAccountInRole(String accountName, String roleName) {
			return this.userRoleStore.IsInRoleAsync(this.userStore.FindByNameAsync(accountName).Result, roleName).Result;
		}

		public void RemoveAccountFromRoles(String accountName, String[] roles) {
			User user = this.userStore.FindByNameAsync(accountName).Result;
			foreach (String roleName in roles) {
				this.userRoleStore.RemoveFromRoleAsync(user, roleName);
			}
		}

		public void UnlockAccount(String accountName) {
			throw new NotImplementedException();
		}

		public void UpdateAccount(IAccount account) {
			throw new NotImplementedException();
		}

		public Boolean ValidateAccount(String accountName, String password) {
			return this.userManager.Find(accountName, password) != null;
		}
	}
}